package TL4 (sysTL, TL) where {

  -- Simple model of a traffic light

  -- Version 4: Use a "next_state" local definition in the module
  --            for improved readability

  interface TL = { ped_button_push :: Action;
                 };

  data TLstates = AllRed
                | GreenNS  | AmberNS
                | GreenE   | AmberE
                | GreenW   | AmberW
                | GreenPed | AmberPed
                deriving (Eq, Bits);

  type Time32 = UInt 5;

  sysTL :: Module TL;
  sysTL =
      module {
          state :: Reg TLstates;
          state <- mkReg AllRed;

          next_green :: Reg TLstates;
          next_green <- mkReg GreenNS;

          secs :: Reg Time32;
          secs <- mkReg 0;

          ped_button_pushed :: Reg Bool;
          ped_button_pushed <- mkReg False;

          let {
              allRedDelay :: Time32;
              allRedDelay = 2;

              amberDelay :: Time32;
              amberDelay = 4;

              ns_green_delay :: Time32;
              ns_green_delay = 20;

              ew_green_delay :: Time32;
              ew_green_delay = 10;

              pedGreenDelay :: Time32;
              pedGreenDelay = 10;

              pedAmberDelay :: Time32;
              pedAmberDelay = 6;

              next_state :: TLstates -> Action;
              next_state  ns = action { state := ns; secs := 0; };
          };
      interface {
          ped_button_push   = ped_button_pushed  := True;
      };
      addRules $
        rules {
          "wait": when True ==> secs := secs + 1
        }

        +>

        rules {
          "fromAllRed":
            when (state == AllRed) && ((secs + 1) >= allRedDelay)
              ==> if ped_button_pushed then
                      action { ped_button_pushed := False; next_state (GreenPed); }
                  else
                      next_state (next_green);


          "fromGreenPed":
            when (state == GreenPed) && ((secs + 1) >= pedGreenDelay)
            ==>  next_state (AmberPed);

          "fromAmberPed":
            when (state == AmberPed) && ((secs + 1) >= pedAmberDelay)
            ==>  next_state (AllRed);

          "fromGreenNS":
            when (state == GreenNS) && ((secs + 1) >= ns_green_delay)
            ==>  next_state (AmberNS);

          "fromAmberNS":
            when (state == AmberNS) && ((secs + 1) >= amberDelay)
            ==>  action { next_state (AllRed); next_green := GreenE; };

          "fromGreenE":
            when (state == GreenE) && ((secs + 1) >= ew_green_delay)
            ==>  next_state (AmberE);

          "fromAmberE":
            when (state == AmberE) && ((secs + 1) >= amberDelay)
            ==>  action { next_state (AllRed); next_green := GreenW; };

          "fromGreenW":
            when (state == GreenW) && ((secs + 1) >= ew_green_delay)
            ==>  next_state (AmberW);

          "fromAmberW":
            when (state == AmberW) && ((secs + 1) >= amberDelay)
            ==>  action { next_state (AllRed); next_green := GreenNS; };
        }
      }
}
